﻿
// **************************************************************************
//    Class Created by Mick Doherty (Dotnetrix) March 2010
//    http://dotnetrix.co.uk/
//
//    ...for GlassUI (an AeroGlass UI Library)
//    http://glassui.codeplex.com/
// **************************************************************************

using System;
using System.Collections;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Reflection;
using System.Security.Permissions;
using System.Windows.Forms;
using GlassUI.Design;

namespace GlassUI
{
    [ProvideProperty("DefaultToGlass", typeof(ToolStrip))]
    [ProvideProperty("GlazeDropDowns", typeof(ToolStrip))]
    [ProvideProperty("GlowColor", typeof(ToolStrip))]
    [ProvideProperty("RaisedToolStrip", typeof(ToolStrip))]
    [Designer(typeof(GlassFormDesigner), typeof(IRootDesigner))]
    public partial class GlassForm : Form, IExtenderProvider
    {
        public GlassForm()
        {
            this.SetStyle(ControlStyles.ResizeRedraw | ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer, true);
            InitializeComponent();
            ToolStripManager.Renderer = new GlassToolStripRenderer();
        }

        #region Public Events

        [Browsable(true),EditorBrowsable(EditorBrowsableState.Always)]
        public new event EventHandler MarginChanged;

        [GlassUIDescription("GlazeClientAreaChangedDescription")]
        public event EventHandler GlazeClientAreaChanged;

        [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "DWM")]
        [GlassUIDescription("DWMCompositionChangedDescription")]
        public event EventHandler DWMCompositionChanged;

        #endregion

        #region Private Instance Variables

        internal Padding margin = Padding.Empty;
        private bool glazeClientArea = true;
        private bool extendCaption = true;
        private bool doubleBufferChildren = true;
        private Color glowColorValue = SystemColors.Highlight;

        #endregion

        #region Public Properties

        [Category("Layout")]
        [DefaultValue(typeof(Padding),"Empty")]
        [GlassUIDescription("GlassMarginDescription")]
        public Padding GlassMargin
        {
            get { return margin; }
            set
            {
                if (margin != value)
                {
                    margin = value;
                    OnMarginChanged(EventArgs.Empty);
                }
            }
        }

        [Category("Appearance")]
        [DefaultValue(true)]
        [GlassUIDescription("GlazeClientAreaDescription")]
        public bool GlazeClientArea
        {
            get 
            {
                bool sheetGlass = (GlassMargin.Left == -1 || GlassMargin.Top == -1 || GlassMargin.Right == -1 || GlassMargin.Bottom == -1);

                if (sheetGlass)
                {
                    return true;
                }
                return glazeClientArea; 
            }
            set
            {
                if (glazeClientArea != value)
                {
                    glazeClientArea = value;
                    OnGlazeClientAreaChanged(EventArgs.Empty);
                }
            }
        }

        [Category("Behavior")]
        [DefaultValue(true)]
        [GlassUIDescription("ExtendCaptionDescription")]
        public bool ExtendCaption
        {
            get { return extendCaption; }
            set { extendCaption = value; }
        }

        [Category("Behavior")]
        [DefaultValue(true)]
        [GlassUIDescription("DoubleBufferChildrenDescription")]
        public bool DoubleBufferChildren
        {
            get { return doubleBufferChildren; }
            set { doubleBufferChildren = value; }
        }

        [Category("Appearance")]
        [DefaultValue(typeof(Color), "Highlight")]
        [GlassUIDescription("GlassFormGlowColorDescription")]
        [SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods")]
        public Color GlowColor
        {
            get { return glowColorValue; }
            set { glowColorValue = value; }
        }

        #endregion

        #region Overidden Methods

        protected override void OnHandleCreated(EventArgs e)
        {
            base.OnHandleCreated(e);
            if (this.DoubleBufferChildren)
                DoubleBufferControls(this);
        }

        protected override void OnMarginChanged(EventArgs e)
        {
            base.OnMarginChanged(e);
            this.OnResize(EventArgs.Empty);
            Invalidate();
            if (MarginChanged != null)
                MarginChanged(this, e);
        }

        protected override void OnPaintBackground(PaintEventArgs e)
        {
            base.OnPaintBackground(e);
            RenderGlass(e.Graphics);
        }

        protected override void ScaleControl(SizeF factor, BoundsSpecified specified)
        {
            base.ScaleControl(factor, specified);
            ScaleMargin(factor, specified);
        }

        [PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
        protected override void WndProc(ref Message m)
        {
            switch (m.Msg)
            {
                case (int)NativeMethods.USER32.WindowMessage.WM_DWMCOMPOSITIONCHANGED:
                case (int)NativeMethods.USER32.WindowMessage.WM_DWMNCRENDERINGCHANGED:
                    OnDWMCompositionChanged(EventArgs.Empty);
                    break;

                case (int)NativeMethods.USER32.WindowMessage.WM_NCHITTEST:
                    base.DefWndProc(ref m);
                    if (m.Result.ToInt32() == 1)
                    {
                        Point pt = this.PointToClient(new Point(m.LParam.ToInt32()));
                        int result = OnNonClientHitTest(pt);
                        if (result != -1)
                        {
                            m.Result = (IntPtr)result;
                            return;
                        }
                    }
                    break;

                case (int)NativeMethods.USER32.WindowMessage.WM_NCRBUTTONUP:
                    if (m.WParam.ToInt32() == 2)
                    {
                        NativeMethods.USER32.SendMessage(this.Handle, NativeMethods.USER32.WindowMessage.WM_GETSYSMENU, IntPtr.Zero, m.LParam);
                    }
                    break;

                default:
                    //Console.WriteLine(m.ToString());
                    break;
            }

            //if (m.Msg == NativeMethods.USER32.WM_NCCALCSIZE)
            //{
            //    Rectangle[] sizeRect;
            //    Console.WriteLine("Enter: ");
            //    Console.WriteLine("---------------------------");
            //    if (m.WParam.ToInt32() == 1)
            //    {
            //        NativeMethods.USER32.NCCALCSIZE_PARAMS lparam = (NativeMethods.USER32.NCCALCSIZE_PARAMS)m.GetLParam(typeof(NativeMethods.USER32.NCCALCSIZE_PARAMS));
            //        sizeRect = new Rectangle[] { 
            //            lparam.rgrc0.ToRectangle(),     // Proposed new window coordinates after move or resize;
            //            lparam.rgrc1.ToRectangle(),     // Window coordinates before move or resize
            //            lparam.rgrc2.ToRectangle() };   // Client area coordinates before move or resize
            //    }
            //    else
            //    {
            //        NativeMethods.USER32.RECT lparam = (NativeMethods.USER32.RECT)m.GetLParam(typeof(NativeMethods.USER32.RECT));
            //        sizeRect = new Rectangle[] { lparam.ToRectangle() };
            //    }
            //    OnNonClientCalcSize(sizeRect);
            //    Console.WriteLine();
            //}


           // Console.WriteLine(m.ToString());

            base.WndProc(ref m);

            if (m.Msg == (int)NativeMethods.USER32.WindowMessage.WM_NCCALCSIZE)
            {
                Rectangle[] sizeRect;
                //Console.WriteLine("Exit: ");
                //Console.WriteLine("---------------------------");
                if (m.WParam.ToInt32() == 1)
                {
                    NativeMethods.USER32.NCCALCSIZE_PARAMS lparam = (NativeMethods.USER32.NCCALCSIZE_PARAMS)m.GetLParam(typeof(NativeMethods.USER32.NCCALCSIZE_PARAMS));
                    sizeRect = new Rectangle[] { 
                        lparam.rgrc0.ToRectangle(),     // new coordinates of Client after move or resize
                        lparam.rgrc1.ToRectangle(),     // valid destination rectangle after move or resize
                        lparam.rgrc2.ToRectangle() };   // valid source rectangle after move or resize
                }
                else
                {
                    NativeMethods.USER32.RECT lparam = (NativeMethods.USER32.RECT)m.GetLParam(typeof(NativeMethods.USER32.RECT));
                    sizeRect = new Rectangle[] { lparam.ToRectangle() };
                }
                OnNonClientCalcSize(sizeRect);
                //Console.WriteLine();
                OnDWMCompositionChanged(EventArgs.Empty);
            }
        }

        #endregion

        #region Protected Methods

        [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "DWM")]
        protected virtual void OnDWMCompositionChanged(EventArgs eventArgs)
        {
            if (GlassUtilities.GlassEnabled)
            {
                ExtendGlassToClient();
                GlazeClient();
            }
            if (DWMCompositionChanged != null)
                DWMCompositionChanged(this, eventArgs);
        }

        protected virtual void OnGlazeClientAreaChanged(EventArgs eventArgs)
        {
            Invalidate();
            if (GlazeClientAreaChanged != null)
                GlazeClientAreaChanged(this, eventArgs);
        }

        protected virtual int OnNonClientHitTest(Point pt)
        {
            if (GlassUtilities.GlassEnabled && ExtendCaption)
            {
                Rectangle topMargin = new Rectangle(0, 0, ClientRectangle.Width, GlassMargin.Top);
                if (topMargin.Contains(pt))
                    return 2;
            }
            return -1;
        }

        protected virtual void OnNonClientCalcSize(Rectangle[] sizeRectangles)
        {
            //foreach (Rectangle rc in sizeRectangles)
            //    Console.WriteLine(rc.ToString());
        }

        protected virtual void ScaleMargin(SizeF factor, BoundsSpecified specified)
        {
            float l = margin.Left * factor.Width;
            float t = margin.Top * factor.Height;
            float r = margin.Right * factor.Width;
            float b = margin.Bottom * factor.Height;
            GlassMargin = new Padding((int)l, (int)t, (int)r, (int)b);
        }

        #endregion

        #region Internal Methods

        internal bool ExtendGlassToClient()
        {
            if (GlassMargin != Padding.Empty)
            {
                NativeMethods.DWMAPI.MARGINS margins = new GlassUI.NativeMethods.DWMAPI.MARGINS(GlassMargin.Left, GlassMargin.Right, GlassMargin.Top, GlassMargin.Bottom);
                return NativeMethods.DWMAPI.DwmExtendFrameIntoClientArea(this.Handle, ref margins).ToInt32() == NativeMethods.USER32.S_OK;
            }
            return false;
        }

        internal bool GlazeClient()
        {
            if (this.GlazeClientArea)
            {
                NativeMethods.DWMAPI.DWM_BLURBEHIND blur = new NativeMethods.DWMAPI.DWM_BLURBEHIND();
                blur.dwFlags = NativeMethods.DWMAPI.DwmBlurBehindFlags.DWM_BB_ENABLE;
                blur.fEnable = true;
                return NativeMethods.DWMAPI.DwmEnableBlurBehindWindow(this.Handle, ref blur).ToInt32() == NativeMethods.USER32.S_OK;
            }
            return false;
        }

        internal void RenderGlass(Graphics graphics)
        {
            bool sheetGlass = (GlassMargin.Left == -1 || GlassMargin.Top == -1 || GlassMargin.Right == -1 || GlassMargin.Bottom == -1);

            Rectangle rc = this.ClientRectangle;
            rc.Offset(GlassMargin.Left, GlassMargin.Top);
            rc.Width -= GlassMargin.Horizontal;
            rc.Height -= GlassMargin.Vertical;

            if (GlassUtilities.GlassEnabled && !this.DesignMode)
            {
                if (!GlazeClientArea && !sheetGlass)
                {
                    graphics.SetClip(rc, CombineMode.Exclude);
                }
                graphics.Clear(Color.Empty);
                if (this.BackColor != Color.Transparent)
                {
                    using (Brush brush = new SolidBrush(Color.FromArgb(Math.Min((int)GlassUtilities.SystemGlassColor.A, 96), this.BackColor)))
                        graphics.FillRectangle(brush, rc);
                }
                graphics.ResetClip();
            }

        }

        internal static void SetDoubleBuffered(Control control)
        {
            PropertyInfo pi = control.GetType().GetProperty("DoubleBuffered", BindingFlags.NonPublic | BindingFlags.Instance);
            if (pi == null)
                return;
            pi.SetValue(control, true, null);
        }

        internal void DoubleBufferControls(Control control)
        {
            if (control.Controls.Count > 0)
            {
                foreach (Control child in control.Controls)
                {
                    SetDoubleBuffered(child);
                    if (child.Controls.Count > 0)
                        DoubleBufferControls(child);
                }
            }
        }

        internal bool ControlIsOnGlass(Control control)
        {
            if (GlassUtilities.GlassEnabled && !this.DesignMode)
            {
                if (control.FindForm() == this)
                {
                    if (this.GlazeClientArea)
                        return true;
                    Padding gm = this.GlassMargin;
                    if (gm.Left == -1 || gm.Top == -1 || gm.Right == -1 || gm.Bottom == -1)
                        return true;

                    Rectangle controlBounds = this.RectangleToScreen(control.Bounds);
                    Rectangle clientBounds = this.ClientRectangle;
                    clientBounds.X += gm.Left;
                    clientBounds.Y += gm.Top;
                    clientBounds.Height -= gm.Vertical;
                    clientBounds.Width -= gm.Horizontal;
                    return !clientBounds.IntersectsWith(controlBounds);
                }
            }
            return false;
        }

        #endregion

        #region IExtenderProvider Members

        public bool CanExtend(object extendee)
        {
            return extendee is ToolStrip;
        }

        private Hashtable defaultToGlass = new Hashtable();
        private Hashtable glazeDropDowns = new Hashtable();
        private Hashtable glowColor = new Hashtable();
        private Hashtable raisedToolStrip = new Hashtable();

        [ExtenderProvidedProperty()]
        [Category("Behavior")]
        [DefaultValue(true)]
        public bool GetDefaultToGlass(ToolStrip target)
        {
            if (defaultToGlass.Contains(target))
                return (bool)(defaultToGlass[target]);
            defaultToGlass.Add(target, true);
            return true;
        }

        public void SetDefaultToGlass(ToolStrip target, bool value)
        {
            if (defaultToGlass.Contains(target))
                defaultToGlass.Remove(target);
            defaultToGlass.Add(target, value);
        }

        [ExtenderProvidedProperty()]
        [Category("Behavior")]
        [DefaultValue(true)]
        public bool GetGlazeDropDowns(ToolStrip target)
        {
            if (glazeDropDowns.Contains(target))
                return (bool)(glazeDropDowns[target]);
            glazeDropDowns.Add(target, true);
            return true;
        }

        public void SetGlazeDropDowns(ToolStrip target, bool value)
        {
            if (glazeDropDowns.Contains(target))
                glazeDropDowns.Remove(target);
            glazeDropDowns.Add(target, value);
        }

        [ExtenderProvidedProperty()]
        [Category("Appearance")]
        public Color GetGlowColor(ToolStrip target)
        {
            if (glowColor.Contains(target))
            {
                Color value = (Color)(glowColor[target]);
                if (value.IsEmpty)
                    return this.GlowColor;
                return value; 
            }
            glowColor.Add(target, Color.Empty);
            return this.glowColorValue;
        }

        public void SetGlowColor(ToolStrip target, Color value)
        {
            if (glowColor.Contains(target))
                glowColor.Remove(target);
            glowColor.Add(target, value);
        }

        [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
        private bool ShouldSerializeGlowColor(ToolStrip target)
        {
            return !((Color)glowColor[target]).IsEmpty;
        }

        [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
        private void ResetGlowColor(ToolStrip target)
        {
            glowColor[target] = Color.Empty;
        }

        [ExtenderProvidedProperty()]
        [Category("Appearance")]
        [DefaultValue(false)]
        public bool GetRaisedToolStrip(ToolStrip target)
        {
            if (raisedToolStrip.Contains(target))
                return (bool)(raisedToolStrip[target]);
            raisedToolStrip.Add(target, false);
            return false;
        }

        public void SetRaisedToolStrip(ToolStrip target, bool value)
        {
            if (raisedToolStrip.Contains(target))
                raisedToolStrip.Remove(target);
            raisedToolStrip.Add(target, value);
        }

        #endregion

    }
}
